package Parse;

import Lex.LexAnalyzer;
import Lex.Symbol;
import Lex.Token;
import com.exception.PL0Exception;

import java.io.File;
import java.util.*;

public class Parser {
    // 存放源代码的token
    private List<Token> allToken;

    // 当前处理的token指针
    private int ptrToken = -1;
    // 当前的token
    private Token currentToken;
    // 当前token的symbol
    private Symbol curSymbol;
    // 当前token的value
    private String curVal;

    // 用于存放所有的常量标识符以及其值
    private Map<String, Integer> constSYM;
    // 存放变量标识符
    private List<String> varSYM;
    // 存放过程标识符
    private List<String> procSYM;


    public Parser(File file) {
        LexAnalyzer lexAnalyzer = new LexAnalyzer(file);
        allToken = lexAnalyzer.getAllToken();
        constSYM = new HashMap<>();
        varSYM = new ArrayList<>();
        procSYM = new ArrayList<>();
    }

    // 语法分析器的入口函数
    public void check() throws PL0Exception {
        // 程序 -> 分程序 .
        step();
        subProgram();

        step();
        if (curSymbol == Symbol.EOF) {

        } else if (curSymbol == Symbol.POI) {
            step();
            while (curSymbol != Symbol.EOF) {
                subProgram();
                step();
            }
        } else {
            throw new PL0Exception("未发现 \".\"", currentToken);
        }
    }

    private void subProgram() throws PL0Exception {
        // <分程序> ::= [<常量说明部分>][变量说明部分>][<过程说明部分>]<语句>
        if (curSymbol == Symbol.CON) {
            constPart();
            step();
        }

        if (curSymbol == Symbol.VAR) {
            varPart();
            step();
        }

        if (curSymbol == Symbol.PROC) {
            procPart();
            step();
        }

        phase();
    }

    public void constPart() throws PL0Exception {
        // <常量说明部分> ::= const<常量定义>{,<常量定义>};
        if (curSymbol == Symbol.CON) {
            while (curSymbol != Symbol.SEMIC) {
                step();
                ConstDefine();
                step();
                if (curSymbol != Symbol.COMMA && curSymbol != Symbol.SEMIC) {
                    throw new PL0Exception("未发现 \";\"", currentToken);
                }
            }
        } else {
            throw new PL0Exception("常量未声明", currentToken);
        }
    }

    public void ConstDefine() throws PL0Exception {
        // <常量定义> ::= <标识符>=<无符号整数>
        SYM();
        String name = curVal;
        step();
        if (curSymbol == Symbol.EQU) {
            step();
            UnsignedNum();
            Integer val = Integer.parseInt(curVal);

            if (constSYM.containsKey(name)) {
                throw new PL0Exception("重复定义", currentToken);
            } else {
                constSYM.put(name, val);
            }
        } else {
            throw new PL0Exception("未发现 \"=\" ", currentToken);
        }
    }

    public void varPart() throws PL0Exception {
        // <变量说明部分>::= var<标识符>{,<标识符>};
        if (curSymbol == Symbol.VAR) {
            while (curSymbol != Symbol.SEMIC) {
                step();
                SYM();
                varSYM.add(curVal);
                step();
                if (curSymbol != Symbol.SEMIC && curSymbol != Symbol.COMMA) {
                    throw new PL0Exception("未发现 \";\"", currentToken);
                }
            }
        } else {
            throw new PL0Exception("未定义变量", currentToken);
        }
    }

    public void procHead() throws PL0Exception {
        // <过程首部> ::= procedure<标识符>;
        if (curSymbol == Symbol.PROC) {
            step();
            SYM();
            procSYM.add(curVal);
            step();
            if (curSymbol != Symbol.SEMIC) {
                throw new PL0Exception("未发现 \";\"", currentToken);
            }
        } else {
            throw new PL0Exception("未定义过程", currentToken);
        }
    }

    public void procPart() throws PL0Exception {
        // <过程说明部分> ::= <过程首部><分程序>;{<过程说明部分>}
        while (curSymbol == Symbol.PROC) {
            procHead();
            step();
            subProgram();
            step();
        }
    }

    public void phase() throws PL0Exception {
        // <语句> ::= <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<重复语句>|<空>
        switch (curSymbol) {
            case SYM -> assignPhase();
            case IF -> ifPhase();
            case WHI -> whilePhase();
            case CAL -> callPhase();
            case REA -> readPhase();
            case WRI -> writePhase();
            case BEG -> combinedPhase();
            case REP -> repeatPhase();
            // default -> flag = false;
        }
    }

    public void assignPhase() throws PL0Exception {
        // <赋值语句> ::= <标识符>:=<表达式>
        if (curSymbol == Symbol.SYM) {
            SYM();
            step();
            if (curSymbol == Symbol.CEQU) {
                step();
                expression();
            } else {
                throw new PL0Exception("缺少 \":=\"", currentToken);
            }
        } else {
            throw new PL0Exception("缺少标识符", currentToken);
        }
    }

    public void expression() throws PL0Exception {
        // <表达式> ::= [+|-]<项>{<加法运算符><项>}
        if (curSymbol == Symbol.ADD || curSymbol == Symbol.SUB) {
            step();
        }
        item();
        step();
        while (curSymbol == Symbol.ADD || curSymbol == Symbol.SUB) {
            step();
            item();
            step();
        }
        stepBack();
    }

    public void item() throws PL0Exception {
        // <项> ::= <因子>{<乘法运算符><因子>}
        factor();
        step();
        while (curSymbol == Symbol.MUL || curSymbol == Symbol.DIV) {
            step();
            factor();
            step();
        }
        stepBack();
    }

    public void factor() throws PL0Exception {
        // <因子> ::= <标识符>|<无符号整数>|'('<表达式>')‘
        if (curSymbol == Symbol.SYM) {
            SYM();
        } else if (curSymbol == Symbol.CONST) {
            UnsignedNum();
        } else if (curSymbol == Symbol.LBR) {
            step();
            expression();
            step();
            if (curSymbol != Symbol.RBR) {
                throw new PL0Exception("缺少 \")\"", currentToken);
            }
        } else {
            throw new PL0Exception("表达式出错", currentToken);
        }
    }

    public void ifPhase() throws PL0Exception {
        // <条件语句> ::= if<条件>then<语句>[else<语句>]
        step();
        condition();
        step();
        if (curSymbol == Symbol.THEN) {
            step();
            phase();
            step();
            if (curSymbol == Symbol.ELS) {
                step();
                phase();
            }
        } else {
            throw new PL0Exception("缺少 \"then\"", currentToken);
        }
        stepBack();
    }

    public void condition() throws PL0Exception {
        // <条件> ::= <表达式><关系运算符><表达式>|odd<表达式>
        if (curSymbol == Symbol.ODD) {
            step();
            expression();
        } else {
            expression();
            step();
            List<Symbol> tmp = Arrays.asList(Symbol.EQU, Symbol.LES, Symbol.LESE,
                    Symbol.LAR, Symbol.LARE, Symbol.NEQU
            );
            if (tmp.contains(curSymbol)) {
                step();
                expression();
            } else {
                throw new PL0Exception("缺少关系运算符", currentToken);
            }
        }
    }

    public void whilePhase() throws PL0Exception {
        // <当型循环语句> ::= while<条件>do<语句>
        step();
        condition();
        step();
        if (curSymbol == Symbol.DO) {
            step();
            phase();
        } else {
            throw new PL0Exception("缺少 \"do\"", currentToken);
        }
    }

    public void callPhase() throws PL0Exception {
        // <过程调用语句> ::= call<标识符>
        step();
        SYM();
    }

    public void readPhase() throws PL0Exception {
        // <读语句> ::= read'('<标识符>{,<标识符>}')‘
        step();
        if (curSymbol == Symbol.LBR) {
            step();
            while (true) {
                SYM();
                step();
                if (curSymbol == Symbol.COMMA) {
                    step();
                } else if (curSymbol == Symbol.RBR) {
                    break;
                } else {
                    throw new PL0Exception("缺少 \")\"", currentToken);
                }
            }
        } else {
            throw new PL0Exception("缺少 \"(\"", currentToken);
        }
    }

    public void writePhase() throws PL0Exception {
        // <写语句> ::= write'('<标识符>{,<标识符>}')‘
        step();
        if (curSymbol == Symbol.LBR) {
            step();
            while (true) {
                if (curSymbol == Symbol.SYM) {
                    SYM();
                } else if (curSymbol == Symbol.CONST) {
                    UnsignedNum();
                }
                step();
                if (curSymbol == Symbol.COMMA) {
                    step();
                } else if (curSymbol == Symbol.RBR) {
                    break;
                } else {
                    throw new PL0Exception("缺少 \")\"", currentToken);
                }
            }
        } else {
            throw new PL0Exception("缺少 \"(\"", currentToken);
        }
    }

    public void combinedPhase() throws PL0Exception {
        // <复合语句> ::= begin<语句>{;<语句>}end
        step();
        phase();
        step();
        if (curSymbol == Symbol.SEMIC) {
            step();
            while (true) {
                phase();
                step();
                if (curSymbol == Symbol.END) {
                    break;
                } else if (curSymbol == Symbol.SEMIC) {
                    step();
                } else {
                    throw new PL0Exception("缺少 \"end\"", currentToken);
                }
            }
        }
    }

    public void repeatPhase() throws PL0Exception {
        // <重复语句> ::= repeat<语句>{;<语句>}until<条件>
        step();
        while (true) {
            phase();
            step();
            if (curSymbol == Symbol.SEMIC) {
                step();
            } else if (curSymbol == Symbol.UNT) {
                break;
            } else {
                throw new PL0Exception("缺少 \"untill\"", currentToken);
            }
        }
        step();
        condition();
    }

    public void SYM() throws PL0Exception {
        // <标识符> ::= <字母>{<字母>|<数字>}
        char[] val = curVal.toCharArray();
        if (Character.isLetter(val[0]) && curSymbol == Symbol.SYM) {
            for (char c : val) {
                if (!Character.isLetter(c) && !Character.isDigit(c)) {
                    throw new PL0Exception("标识符中存在非法字符", currentToken);
                }
            }
        } else {
            throw new PL0Exception("非法的标识符首字符", currentToken);
        }
    }

    public void UnsignedNum() throws PL0Exception {
        // <无符号整数> ::= <数字>{<数字>}
        if (curSymbol != Symbol.CONST) {
            throw new PL0Exception("表达式出错", currentToken);
        } else {
            char[] val = curVal.toCharArray();
            for (char c : val) {
                if (!Character.isDigit(c)) {
                    throw new PL0Exception("非法的数值", currentToken);
                }
            }
        }
    }

    // 指针向前移动
    public void step() {
        ptrToken++;
        if (ptrToken < allToken.size()) {
            currentToken = allToken.get(ptrToken);
            curSymbol = currentToken.getSymbol();
            curVal = currentToken.getValue();
        } else currentToken = null;
    }

    // 指针向后移动
    public void stepBack() {
        ptrToken--;
        if (ptrToken < allToken.size()) {
            currentToken = allToken.get(ptrToken);
            curSymbol = currentToken.getSymbol();
            curVal = currentToken.getValue();
        } else {
            currentToken = null;
        }
    }
}



